from dataclasses import dataclass
from datetime import datetime
from typing import List, Dict, Union, Callable

from pyecharts.charts import Line
import pyecharts.options as opts

from emi.bar.data import BarData, Interval, BarBase
from emi.util import SameTimeBarList, BarUtils


@dataclass
class MutiLineChart:
    """
    多个线比较。
    """

    @dataclass
    class LineItemData:
        name: str
        values: [] = None
        color = None

        def __post_init__(self):
            if self.values is None:
                self.values = []

    name : ""
    x_values:[] = None
    y_values:List[LineItemData] = None

    def __post_init__(self):
        if self.x_values is None:
            self.x_values = []
        if self.y_values is None:
            self.y_values = []

    @staticmethod
    def make(title:str,bar_map:Dict[str,List[Union[BarBase]]],is_daily_time:bool,line_value:Callable = None):
        """
        如1：实时涨幅pnl比较
        chart = MutiLineChart.make("实时涨幅pnl比较",
                     bar_map=d_bars_map,
                     is_daily_time=False,
                     line_value=lambda bar:bar.pnl)
                     .build_chart()
        如1：rank排行值比较
        chart = MutiLineChart.make("rank排行值比较",
                     bar_map=d_bars_map,
                     is_daily_time=False,
                     line_value=lambda rank:rank.value
                     )
                     .build_chart()
        构建数据对象。
        """
        if not line_value:
            _bar_map_2 = {}
            for name, bars in bar_map.items():
                if bars:
                    ### 统一line值，以便在同一维度上面做比较
                    base_value = 1000.0
                    _bar_list = []
                    for bar in bars:
                        _copy_bar:BarData = BarData.copy(bar,BarData)
                        _copy_bar.reset_price(base_value)
                        base_value = _copy_bar.close
                        _bar_list.append(_copy_bar)
                    _bar_map_2[name] = _bar_list
            bar_map = _bar_map_2

        line_item_map:Dict[str,MutiLineChart.LineItemData]= {}
        same_time_barmap = SameTimeBarList(is_daily=is_daily_time)
        item_symbol_list = []
        for name,bars in bar_map.items():
            if bars:
                line_item = MutiLineChart.LineItemData(name=name)
                line_item.bars = bars
                line_item.time_bar_map = {}
                symbol = bars[0].symbol
                line_item_map[symbol] = line_item
                item_symbol_list.append(symbol)
                for bar in bars:
                    same_time_barmap.put(bar)
                    _time_key = same_time_barmap.time_to_key(bar.datetime)
                    line_item.time_bar_map[_time_key] = bar
        same_time_map = same_time_barmap.to_map()
        time_list = list(same_time_map.keys())
        time_list.sort()
        x_values = []

        def _get_value(bar:BarBase):
            if line_value:
                v = line_value(bar)
                if isinstance(v,set):
                    return v.pop()
                return v
            return bar.close




        for time in time_list:
            time:datetime = time
            _time_key = same_time_barmap.time_to_key(time)
            if is_daily_time:
                x_values.append(f"{time.strftime('%Y/%m/%d')}")
            else:
                ### buyfix: 9点30（包含）以前数据跳过
                if time.hour == 9 and time.minute <=30:
                    continue
                x_values.append(f"{time.strftime('%Y/%m/%d %H:%M')}")
            ###某个时间点的值
            for symbol in item_symbol_list:
                line_item : MutiLineChart.LineItemData = line_item_map[symbol]
                bar:BarBase =  line_item.time_bar_map.get(_time_key)

                if bar:
                    line_item.values.append(_get_value(bar))
                else:
                    if len(line_item.values) > 0:
                        line_item.values.append(line_item.values[-1])  ## 使用上一个
                    else:
                        line_item.values.append(_get_value(line_item.bars[0]))
        multi_data = MutiLineChart(name=title, x_values=x_values, y_values = list(line_item_map.values()))
        return multi_data

    def build_chart(self):
        """
        创建线性图看盘。】

        """
        data = self
        line_panle = Line()
        line_panle.add_xaxis(data.x_values)

        for line_data in data.y_values:
            line_panle.add_yaxis(
                line_data.name,
                line_data.values,
                symbol="dot",
                label_opts=opts.LabelOpts(is_show=False),
                itemstyle_opts=opts.ItemStyleOpts(color=MutiLineChart.LineItemData.color, color0=MutiLineChart.LineItemData.color),
            )
        _range_start = 50

        line_panle.set_global_opts(
            title_opts=opts.TitleOpts(title=data.name, pos_top="95%"),
            legend_opts=opts.LegendOpts(is_show=True),
            yaxis_opts=opts.AxisOpts(
                type_="value",
                splitline_opts=opts.SplitLineOpts(
                    is_show=True, linestyle_opts=opts.LineStyleOpts(opacity=1)
                ),
            ),
            xaxis_opts=opts.AxisOpts(
                type_="category",
                axistick_opts=opts.AxisTickOpts(is_align_with_label=True),
                axisline_opts=opts.AxisLineOpts(
                    is_on_zero=False, linestyle_opts=opts.LineStyleOpts(color="#d14a61")
                ),
                axispointer_opts=opts.AxisPointerOpts(
                    is_show=True,
                    label=opts.LabelOpts(is_show=True)
                ),
            ),
            datazoom_opts=[
                opts.DataZoomOpts(
                    is_show=False,
                    type_="inside",
                    #xaxis_index=[0, 1],
                    range_start=_range_start,
                    range_end=100,
                ),
                opts.DataZoomOpts(
                    is_show=True,
                    #xaxis_index=[0, 1],
                    type_="slider",
                    pos_top="90%",
                    range_start=_range_start,
                    range_end=100,
                ),
                opts.DataZoomOpts(is_show=False,
                                  #xaxis_index=[0, 1],
                                  range_start=_range_start,
                                  range_end=100),

            ],
        )

        return line_panle